#!usr/bin/env python
# coding=utf-8

__author__ = 'yujun huang'

from abc import ABC

from utils import router
import os
import glob
import json
import shutil
import zipfile
import logging
from handlers.base_handler import BaseHandler
from utils import path
from aiofile import async_open


def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None):
    """Create a zip file from all the files under 'base_dir'.

    The output zip file will be named 'base_name' + ".zip".  Returns the
    name of the output zip file.
    """

    zip_filename = base_name + ".zip"
    archive_dir = os.path.dirname(base_name)

    if archive_dir and not os.path.exists(archive_dir):
        if logger is not None:
            logger.info("creating %s", archive_dir)
        if not dry_run:
            os.makedirs(archive_dir)

    if logger is not None:
        logger.info("creating '%s' and adding '%s' to it",
                    zip_filename, base_dir)

    if not dry_run:
        with zipfile.ZipFile(zip_filename, "w",
                             compression=zipfile.ZIP_DEFLATED) as zf:
            _path = os.path.normpath(base_dir)
            if _path != os.curdir:
                zf.write(_path, _path.replace(archive_dir + "/", ""))
                if logger is not None:
                    logger.info("adding '%s'", _path)
            for dirpath, dirnames, filenames in os.walk(base_dir):
                for name in sorted(dirnames):
                    _path = os.path.normpath(os.path.join(dirpath, name))
                    zf.write(_path, _path.replace(archive_dir + "/", ""))
                    if logger is not None:
                        logger.info("adding '%s'", _path)
                for name in filenames:
                    _path = os.path.normpath(os.path.join(dirpath, name))
                    if os.path.isfile(_path):
                        zf.write(_path, _path.replace(archive_dir + "/", ""))
                        if logger is not None:
                            logger.info("adding '%s'", _path)

    return zip_filename


@router.Route("/upload")
class FileUploadUiHandler(BaseHandler, ABC):

    def get(self):
        self.render('file_upload.html')


@router.Route("/console/file_upload")
class FileUploadHandler(BaseHandler):

    def post(self):
        complect = int(self.get_argument('complect'))
        task_id = self.get_argument('task_id')
        chunk = self.get_argument('chunk', 0)
        temp_path = path.get_temp_path()
        if not complect:
            upload_file = self.request.files['file'][0]
            body = upload_file['body']
            temp_filename = "%s%s" % (task_id, chunk)
            with open(os.path.join(temp_path, temp_filename), 'wb') as fp:
                fp.write(body)
        else:
            logging.info('upload complect task_id: {}'.format(task_id))
            filename = self.get_argument('filename')
            filename_path = os.path.join(path.get_upload_path(), filename)
            filename_path = path.check_repeat_filename(filename_path)
            with open(filename_path, 'wb') as upload_file:
                wipe_temps = list()
                while True:
                    temp_filename = "%s%s" % (task_id, chunk)
                    temp_filename_path = os.path.join(temp_path, temp_filename)
                    if os.path.exists(temp_filename_path):
                        temp_file = open(temp_filename_path, 'rb')
                        upload_file.write(temp_file.read())
                        temp_file.flush()
                        temp_file.close()
                        logging.info('upload flushed temp file: {}'.format(
                            temp_filename))
                        wipe_temps.append(temp_filename_path)
                    else:
                        logging.info(
                            'upload successfully file:{}'.format(filename))
                        logging.info('removing temp files')
                        for filename_path in wipe_temps:
                            os.remove(filename_path)
                        logging.info('remove complect')
                        break
                    chunk += 1


@router.Route("/")
class FilesViewHandler(BaseHandler, ABC):

    def get(self, share_path=None):
        if share_path is None:
            share_path = path.get_share_path()
        file_or_dirs = glob.glob(os.path.join(share_path, '*'))
        filepaths = [file_or_dir for file_or_dir in file_or_dirs
                     if not os.path.isdir(file_or_dir)]
        dirs = [file_or_dir for file_or_dir in file_or_dirs
                if os.path.isdir(file_or_dir)]
        filepaths.sort(key=lambda d: (os.path.getmtime(d), d.lower()),
                       reverse=True)
        dirs.sort(key=lambda d: (os.path.getmtime(d), d.lower()), reverse=True)

        datas = [dict(type="dir",
                      display_name=path.get_display_name(_dir),
                      link_name=path.get_link_name(_dir)) for _dir in dirs]
        datas.extend([dict(type="file",
                           display_name=path.get_display_name(filepath),
                           link_name=path.get_link_name(filepath))
                      for filepath in filepaths])

        self.render("view_files.html", **{"datas": datas})


@router.Route("/console/download_file")
class FileDownloadHandler(FilesViewHandler):

    async def get(self, share_path=None):
        if share_path is None:
            share_path = path.get_share_path()
        link_name = self.get_argument("link_name")
        file_type = self.get_argument("type")
        pack_download = self.get_argument("pack_download", None)
        logging.info(f"download request: {link_name}")
        file_or_dir_path = os.path.join(share_path, link_name)
        filename = os.path.basename(link_name)
        if file_type == "dir":
            if not pack_download:
                return super().get(file_or_dir_path)
            else:
                # zip_cmd = f"zip -r {file_or_dir_path}.zip {file_or_dir_path}"
                # os.system(zip_cmd)
                # file_or_dir_path += ".zip"
                file_or_dir_path = _make_zipfile(
                    file_or_dir_path,
                    base_dir=file_or_dir_path,
                )
                filename = os.path.basename(file_or_dir_path)

        self.set_header("Content-type", "application/octet-stream")
        self.set_header("Content-disposition",
                        "attachment; "
                        "filename={}".format(filename).encode("utf8"))

        buf_size = 1024 * 1024
        async with async_open(file_or_dir_path, 'rb') as _input:
            while True:
                by = await _input.read(buf_size)
                if not by:
                    break
                self.write(by)
        # with open(file_or_dir_path, 'rb') as _input:
        #     shutil.copyfileobj(_input, self)
        #
        # self.finish()


if __name__ == '__main__':
    pass
